Skip to content

Implement partial success logging in OTLP exporters#4805

Closed
michaelsafyan wants to merge 12 commits intoopen-telemetry:mainfrom
michaelsafyan:issue4803_partial_success_logging
Closed

Implement partial success logging in OTLP exporters#4805
michaelsafyan wants to merge 12 commits intoopen-telemetry:mainfrom
michaelsafyan:issue4803_partial_success_logging

Conversation

@michaelsafyan
Copy link
Copy Markdown

@michaelsafyan michaelsafyan commented Nov 11, 2025

Log partial success

Partial successes during OTLP export is now logged.

Fixes #4803

Type of change

  • New feature (non-breaking change which adds functionality)
  • [?] This change requires a documentation update

How Has This Been Tested?

Added new unit tests covering the relevant cases.

Does This PR Require a Contrib Repo Change?

  • No.

Checklist:

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • [] Documentation has been updated - unsure what to update

@michaelsafyan michaelsafyan marked this pull request as ready for review November 11, 2025 20:55
@michaelsafyan michaelsafyan requested a review from a team as a code owner November 11, 2025 20:55
@michaelsafyan michaelsafyan changed the title [DRAFT] Implement partial success logging to tackle issue 4803 Implement partial success logging in OTLP exporters Nov 11, 2025
…levels to avoid changing default behaviors. Apply test and formatting fixes.
This appears to cause an issue with "tox -e docs".
Copy link
Copy Markdown
Contributor

@DylanRussell DylanRussell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why put this log behind an env var ? Why not just always log when the field is set -- I'm assuming it's only set when there was a partial success -- it doesn't sound too noisy to me.

@michaelsafyan
Copy link
Copy Markdown
Author

Why put this log behind an env var ? Why not just always log when the field is set -- I'm assuming it's only set when there was a partial success -- it doesn't sound too noisy to me.

I'm uncertain as to the level of noisiness, and so I wanted to minimize disruption to existing users. I could imagine it being somewhat annoying if every batch included one persistently malformed span that just ended up creating log spam.

A more sophisticated approach might be to opt-in to recording every partial success, while elevating the verbosity and logging the partial success unconditionally if every record was a failure (if num_rejected_{type} matches the number of items in the batch). But I wanted to keep things relatively simple.

@DylanRussell
Copy link
Copy Markdown
Contributor

We already have the DuplicateFilter on this logger, maybe we should tweak that to de-dupe based on line no and module instead of message which can be unique so that it filters stuff out better.

I think it's a bit weird to put just 1 log message behind this env var

@michaelsafyan
Copy link
Copy Markdown
Author

Thanks for the review, @DylanRussell . I updated the logic to no longer make the logging conditioned on OTEL_LOG_LEVEL. I'm not sure what the DuplicateFilter is you are referring to ... are you asking to also remove the special casing for logs (because you are not concerned about partial success causing a loop?)

) -> ExportServiceRequestT:
pass

def _log_partial_success(self, partial_success):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we just inline this code instead of having a function (same thing below)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We add a DuplicateFilter to the logger on line 116 (as an aside I don't like the github code review tool, which doesn't let me add a comment to a line that wasn't modified...)

I'm thinking we should update that to reference record.lineno instead of record.msg which could be unique.... and that is sufficient to suppress noisy logging, and also avoid the endless logging issues...

If you want to take a stab at that change here that'd be great, if not I think we can proceed without that and I can make it separately..

I don't think we should special case the logging exporter because we have this duplicate filter thing.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @DylanRussell ! Updated accordingly. Please take another look.

Also, any idea what's going on with the failed tests? They look to be unrelated to this change:
Screenshot Capture - 2025-11-24 - 21-56-05

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's some sort of flake we get all the time (can't find a commit hash), IDK why we get that. It's OK to ignore

) -> ExportServiceRequestT:
pass

def _process_response(self, response):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we inline this code instead ? IMO it's more readable

@@ -46,17 +46,36 @@ class DuplicateFilter(logging.Filter):
recursion depth exceeded issue in cases where logging itself results in an exception."""

def filter(self, record):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO I would simplify all this and just have the logic as:

        current_log_by_line = (
            record.module,
            record.pathname,
            record.lineno,
            time_boundary,
        )
        previous_log_by_line = getattr(self, "last_source", None)
        keep_processing = current_log_by_line != previous_log_by_line
        self.last_source = current_log_by_line 
        return keep_processing

Although I'm realizing now that we should store the previous 10 previous_log_by_line in like a deque or OrderedDict instead.. Right now if duplicate log messages are interleaved we will not filter anything out

Also we have tests for this thing here:

class TestCommonFuncs(unittest.TestCase):
def test_duplicate_logs_filter_works(self):
test_logger = logging.getLogger("testLogger")
test_logger.addFilter(DuplicateFilter())
with self.assertLogs("testLogger") as cm:
test_logger.info("message")
test_logger.info("message")
self.assertEqual(len(cm.output), 1)

# See the License for the specific language governing permissions and
# limitations under the License.


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can revert this

@@ -11,6 +11,7 @@
# See the License for the specific language governing permissions and
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tammy-baylis-swi tammy-baylis-swi moved this to Reviewed PRs that need fixes in Python PR digest Feb 26, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 4, 2026

This PR has been automatically marked as stale because it has not had any activity for 14 days. It will be closed if no further activity occurs within 14 days of this comment.
If you're still working on this, please add a comment or push new commits.

@github-actions github-actions Bot added the Stale label Mar 4, 2026
@github-actions
Copy link
Copy Markdown

This PR has been closed due to inactivity. Please reopen if you would like to continue working on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Make it possible to opt-in to logging partial errors

3 participants